package com.itheima.spring.cyclicDependency2.main;

import com.itheima.spring.cyclicDependency2.bean.OrderService;
import com.itheima.spring.cyclicDependency2.bean.UserService;
import com.itheima.spring.cyclicDependency2.proxy.MyProxyBeanPostProcessor;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 解决循环依赖
 */
public class MainClass3 {

    public static Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
    //一级缓存
    public static Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
    //二级缓存
    public static Map<String, Object> earlySingletonObjects = new HashMap<>(16);
    //三级缓存
    public static Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

    //创建过代理对象的原始对象 beanName-bean
    private final Map<Object, Object> earlyProxyReferences = new ConcurrentHashMap<>(16);

    //正在创建的bean的bean名称
    public static Set<String> singletonCurrentlyInCreation = new HashSet<>();

    //模拟注册BeanDefinition
    public static void loadBeanDefinitionMap() {
        BeanDefinition bd1 = new RootBeanDefinition(UserService.class);
        BeanDefinition bd2 = new RootBeanDefinition(OrderService.class);
        beanDefinitionMap.put("userService", bd1);
        beanDefinitionMap.put("orderService", bd2);
    }

    public static void main(String[] args) {
        loadBeanDefinitionMap();
        beanDefinitionMap.forEach((beanName, beanDefinition) -> {
            try {
                Object bean = getBean(beanName);
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }

    public static Object getBean(String beanName) throws Exception {
        Object instanceBean;
        instanceBean = getSingleton(beanName);
        if (instanceBean != null) {
            return instanceBean;
        }

        //创建bean
        RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionMap.get(beanName);
        Class<?> beanClass = beanDefinition.getBeanClass();
        instanceBean = beanClass.newInstance();
        final Object bean = instanceBean;
        singletonCurrentlyInCreation.add(beanName);
        singletonFactories.put(beanName, () -> new MyProxyBeanPostProcessor().getEarlyBeanReference(bean, beanName));

        //填充属性
        Field[] declaredFields = beanClass.getDeclaredFields();
        for (Field f : declaredFields) {
            Autowired autowired = f.getAnnotation(Autowired.class);
            if (autowired != null) {
                //禁用安全检查，私有属性，保证可见；不禁用会报错
                f.setAccessible(true);
                //从Spring 获取依赖对象
                Object object = getBean(f.getName());
                f.set(instanceBean, object);
            }
        }

        //初始化 略

        //模拟需要AOP则从二级缓存拿代理对象
        if (earlySingletonObjects.containsKey(beanName)) {
            instanceBean = earlySingletonObjects.get(beanName);
        }
        singletonObjects.put(beanName, instanceBean);


        //删除二三级缓存，删除创建中集合beanName
        singletonFactories.remove(beanName);
        earlySingletonObjects.remove(beanName);
        singletonCurrentlyInCreation.remove(beanName);


        return instanceBean;
    }

    public static Object getSingleton(String beanName) {
        //c从单例池拿
        Object instanceBean = singletonObjects.get(beanName);
        //单例池没拿到对象，而且发生循环依赖就从二级缓存拿
        if (instanceBean == null && singletonCurrentlyInCreation.contains(beanName)) {
            instanceBean = earlySingletonObjects.get(beanName);
            if (instanceBean == null) {
                //二级缓存拿不到对象就从三级缓存拿
                ObjectFactory<?> objectFactory = singletonFactories.get(beanName);
                if (objectFactory != null) {
                    instanceBean = objectFactory.getObject();
                    earlySingletonObjects.put(beanName, instanceBean);
                    singletonFactories.remove(beanName);
                }
            }
        }
        return instanceBean;

    }

}
